
#include "sc_gui.h"

static const int16_t sin0_90_table[] =
{
    0,     572,   1144,  1715,  2286,  2856,  3425,  3993,  4560,  5126,  5690,  6252,  6813,  7371,  7927,  8481,
    9032,  9580,  10126, 10668, 11207, 11743, 12275, 12803, 13328, 13848, 14364, 14876, 15383, 15886, 16383, 16876,
    17364, 17846, 18323, 18794, 19260, 19720, 20173, 20621, 21062, 21497, 21925, 22347, 22762, 23170, 23571, 23964,
    24351, 24730, 25101, 25465, 25821, 26169, 26509, 26841, 27165, 27481, 27788, 28087, 28377, 28659, 28932, 29196,
    29451, 29697, 29934, 30162, 30381, 30591, 30791, 30982, 31163, 31335, 31498, 31650, 31794, 31927, 32051, 32165,
    32269, 32364, 32448, 32523, 32587, 32642, 32687, 32722, 32747, 32762, 32767
};

/*******************************************
 * Return with sinus of an angle
 * @param angle
 * @return sinus of 'angle'. sin(-90) = -32767, sin(90) = 32767
 */
int16_t sc_sin(int16_t angle)
{
    int16_t ret = 0;
    angle       = angle % 360;
    if(angle < 0) angle = 360 + angle;
    if(angle < 90)
    {
        ret = sin0_90_table[angle];
    }
    else if(angle >= 90 && angle < 180)
    {
        angle = 180 - angle;
        ret   = sin0_90_table[angle];
    }
    else if(angle >= 180 && angle < 270)
    {
        angle = angle - 180;
        ret   = -sin0_90_table[angle];
    }
    else     /*angle >=270*/
    {
        angle = 360 - angle;
        ret   = -sin0_90_table[angle];
    }
    return ret;
}
int16_t sc_cos(int16_t angle)
{
    return -sc_sin(angle-90);
}
// Compute the fixed point square root of an integer and
// return the 8 MS bits of fractional part.
// Quicker than sqrt() for processors that do not have an FPU (e.g. RP2040)
uint8_t sc_sqrt(uint32_t num)
{
    if (num > (0x40000000)) return 0;
    uint32_t bsh = 0x00004000;
    uint32_t fpr = 0;
    uint32_t osh = 0;
    uint32_t bod;
    // Auto adjust from U8:8 up to U15:16
    while (num>bsh)
    {
        bsh <<= 2;
        osh++;
    }
    do
    {
        bod = bsh + fpr;
        if(num >= bod)
        {
            num -= bod;
            fpr = bsh + bod;
        }
        num <<= 1;
    }
    while(bsh >>= 1);
    return fpr>>osh;
}

/**角度斜率计算函数，内部调用返*/
static inline int32_t sc_atan2_slope(int cx, int cy, int x, int y)
{
    int32_t slope;
    x -= cx;
    y -= cy;
    if(y==0)
    {
        slope=x>0?-32767:32767;
    }
    else
    {
        slope=x*256/y;
    }
    if(y>0)   slope+=32767;
    else      slope-=32767;
    return slope;
}

//画弧角上圆头无浮点运算坐标会有一定误差
void SC_arc_sin_cos(SC_arc *p,SC_arc *arc,int16_t Angle)
{
    arc->r=(p->r-p->ir)/2;
    if(0==arc->r) return;

    int32_t sin = -sc_sin(Angle);
    int32_t cos = sc_sin(Angle+90);
    int32_t r1  = (p->r+p->ir)/2;
    if(sin<0)
    {
        arc->cx =(sin*r1-16348)/32768;  //圆心坐标4舍5入+0.5
    }
    else
    {
        arc->cx =(sin*r1+16348)/32768;
    }
    if(cos<0)
    {
        arc->cy = (cos*r1-16348)/32768;
    }
    else
    {
        arc->cy = (cos*r1+16348)/32768;
    }
    arc->cx+=p->cx;
    arc->cy+=p->cy;
    arc->ir= 0;
}

/**fun: pfb画圆*/
int SC_pfb_DrawRound(SC_tile *dest,SC_arc *p,int startAngle, int endAngle,uint16_t ac,uint16_t bc)
{
    if(p->r==0) return 0;
    uint16_t alpha;
    uint16_t r =p->r+1;
     //===========计算相交===============
    SC_ARER intersection;
    if(!SC_pfb_intersection(dest,&intersection,p->cx-r,p->cy-r,p->cx+r,p->cy+r))
    {
        return 0;
    }
    int r1 =   p->ir*p->ir;
    int r2 =   p->r*p->r;
    int rmax = (p->r+1)*(p->r+1);
    int rmin=  p->ir>0? (p->ir-1)*(p->ir-1):0;
    int x,y,dy2,temp;
    int is_arc=(startAngle!=0||endAngle!=360)?1:0;
    int32_t startNorm,endNorm,normSlope;
    if(is_arc)
    {
        // 获取起始和结束角度的归一化斜率
        startNorm = sc_atan2_slope( 0, 0,-sc_sin(startAngle),  sc_sin(startAngle+90));
        endNorm =   sc_atan2_slope( 0, 0,  -sc_sin(endAngle),  sc_sin(endAngle+90));

    }
    for (y = intersection.ys; y<=intersection.ye; y++)
    {
        dy2 = (y-p->cy)*(y-p->cy);
        int dest_offs=(y-dest->ys) * dest->w-dest->xs;
        for ( x = intersection.xs; x <=intersection.xe; x++)
        {
            temp =(x-p->cx)*(x-p->cx) + dy2;
            if (temp>=rmax)
            {
               if(x>p->cx) break;
                continue;                       //大于rmax外圆跳过
            }
            if (temp<rmin)
            {
                if(x<p->cx) x=p->cx*2-x;       //小于于rmin内圆跳过
                continue;
            }
            if (temp<r1)
            {
                alpha= sc_sqrt(temp);           //内边缘
            }
            else if (temp> r2)
            {
                alpha=255-sc_sqrt(temp);        //外边缘
            }
            else
            {
                alpha=255;
            }
            if(is_arc)
            {
                normSlope = sc_atan2_slope(p->cx, p->cy, x, y);   //斜率归一化
                if(startNorm<=endNorm)
                {
                    if(normSlope >= startNorm && normSlope <= endNorm)
                    {
                        set_pixel_value(dest,dest_offs+x,alpha*gui->alpha/255,bc);
                        continue;
                    }
                }
                else
                {
                    if(normSlope >= startNorm || normSlope <= endNorm)
                    {
                        set_pixel_value(dest,dest_offs+x,alpha*gui->alpha/255,bc);
                        continue;
                    }
                }
            }
            set_pixel_value(dest,dest_offs+x,alpha,ac);
        }
    }
    return 1;
}
/**fun: pfb画圆弧进度条*/
void SC_pfb_DrawArc(SC_tile *dest,SC_arc *p,int startAngle, int endAngle,uint16_t ac,uint16_t bc)
{
    SC_tile pfb= {0};
    SC_arc arc[2];
__PFB_DrawArc:
    if(dest==NULL||pfb.buf)
    {
        SC_pfb_clip(&pfb,p->cx-p->r,p->cy-p->r,p->cx+p->r,p->cy+p->r,gui->bkc);  //创建局部pfb
        dest=&pfb;
    }
    if(SC_pfb_DrawRound(dest, p,startAngle,endAngle, ac, bc))
    {
        SC_arc_sin_cos(p,&arc[0],startAngle);
        SC_arc_sin_cos(p,&arc[1],endAngle);
        SC_pfb_DrawRound(dest, &arc[0],0,360, ac, ac);
        SC_pfb_DrawRound(dest, &arc[1],0,360, ac, ac);
    }
    if(pfb.buf)
    {
        if(SC_pfb_Refresh(&pfb,0))
        {
            goto __PFB_DrawArc;
        }
    }
}

/**fun: pfb圆角矩形*/
void SC_pfb_RoundFrame(SC_tile *dest,int xs,int ys,int xe,int ye, int r,int ir, uint16_t ac,uint16_t bc)
{
    SC_tile pfb= {0};
    int r1 =   ir*ir;
    int r2 =   r*r;
    int rmax = (r+1)*(r+1);
    int rmin=  ir>0? (ir-1)*(ir-1):0;
    int ax=xs+r,ay=ys+r;
    int bx=xe-r,by=ye-r;
    int w=bx-ax, h=by-ay;
__PFB_RoundFrame:
    if(dest==NULL||pfb.buf)
    {
        //创建局部pfb
        SC_pfb_clip(&pfb,xs,ys,xe,ye,gui->bkc);
        dest=&pfb;
    }
    SC_ARER intersection;
    if(!SC_pfb_intersection(dest,&intersection,xs,ys,xe,ye))
    {
        return ;
    }
    int dy2,temp;
    int x,y,cx,cy;
    uint16_t alpha;
    for ( y = intersection.ys,cy=ay; y<=intersection.ye; y++)
    {
        int dest_offs=(y-dest->ys) * dest->w-dest->xs;
        if(h>0&&y>ay)
        {
            cy=by;
            if(y<cy)      //(y>ay&&y<cy)       //中间填充
            {
                for ( x = intersection.xs; x <=intersection.xe; x++)
                {
                    if(x<=ax-ir||x>=bx+ir)     //左右垂直线，线宽判断
                    {
                        set_pixel_value(dest,dest_offs+x,gui->alpha,ac);
                    }
                    else
                    {
                        if(bc!=gui->bkc&&x<=gui->lcd_area.xe&& y>=gui->lcd_area.ys)
                        {
                            set_pixel_value(dest,dest_offs+x,gui->alpha,bc);
                        }

                    }
                }
                continue;
            }
        }
        dy2 = (y-cy)*(y-cy);
        int cnt=0;
        for ( x = intersection.xs,cx=ax; x <=intersection.xe; x++)
        {
            if(w>0&&x>ax)
            {
                cx=bx;
                if(x<cx)                    //(x>ax&&x<bx)
                {
                    if(dy2>=r1)             //上下水平直线，线宽判断
                    {
                        set_pixel_value(dest,dest_offs+x,gui->alpha,ac);
                    }
                    else
                    {
                        if(bc!=gui->bkc&&x<=gui->lcd_area.xe&& y>=gui->lcd_area.ys)
                        {
                            set_pixel_value(dest,dest_offs+x,gui->alpha,bc);
                        }

                    }
                    continue;
                }
            }
            temp =(x-cx)*(x-cx) + dy2;
            if (temp>=rmax)
            {
                if(x>cx) break;
                continue;                       //大于rmax外圆跳过
            }
            if (temp<r1)
            {
                if(bc!=gui->bkc&&x<=gui->lcd_area.xe&& y>=gui->lcd_area.ys)
                {
                    set_pixel_value(dest,dest_offs+x,gui->alpha,bc);
                }
                if (temp>rmin)
                {
                    alpha= sc_sqrt(temp);       //内边缘抗锯齿
                    set_pixel_value(dest,dest_offs+x,alpha*gui->alpha/255,ac);
                }
                continue;
            }
            else if (temp> r2)
            {
                alpha=255-sc_sqrt(temp);        //外边缘抗锯齿
                set_pixel_value(dest,dest_offs+x,alpha*gui->alpha/255,ac);
            }
            else
            {
                alpha=255;
                set_pixel_value(dest,dest_offs+x,gui->alpha,ac);
            }
        }
    }
    if(pfb.buf)
    {
        if(SC_pfb_Refresh(&pfb,0))
        {
            goto __PFB_RoundFrame;
        }
    }
}




/***************************************************************************************
** Function name:        SC_pfb_RoundBar
** Description:          画一个圆角进度条
***************************************************************************************/
void SC_pfb_RoundBar(SC_tile *dest,int xs,int ys,int w,int h,int r,int ir,uint16_t ac,uint16_t bc,int vol,int max)
{
    int xe=xs+w-1;
    int ye=ys+h-1;
    if(r>w/2) r=w/2;
    if(r>h/2) r=h/2;
    if(w>=h)
    {
        gui->lcd_area.xe= (xs)+vol*w/max-1;
        SC_pfb_RoundFrame(dest,xs,ys,xe,ye, r, ir,  ac, bc);
        gui->lcd_area.xe=LCD_SCREEN_WIDTH-1;
    }
    else
    {
        gui->lcd_area.ys= (ys)+ max-vol*h/max-1;
        SC_pfb_RoundFrame(dest,xs,ys,xe,ye, r, ir,  ac, bc);
        gui->lcd_area.ys=0;
    }
}


/**fun: pfb画水平线*/
void SC_pfb_draw_line_x(SC_tile *dest,int xs,int ys,uint16_t *src,int len,int mirror)
{
    if(ys<dest->ys||ys>= dest->ys+dest->h) return;  //y边界限定
    int x1= SC_MAX(xs,dest->xs);                    //xs边界限定
    int x2= SC_MIN(xs+len,dest->xs+dest->w);        //xe边界限定
    if(x1>=x2) return;
    uint16_t *pixel=((uint16_t*)dest->buf);
    int ofsx = x1-dest->xs;
    int ofsy = ys-dest->ys;
    src+= mirror>=0? x1-xs:len-1-(x1-xs);   //src 偏移
    pixel+= ofsy*dest->w + ofsx;            //dest 偏移
    for(xs=x1; xs<x2; xs++)
    {
        *pixel= *src;
        pixel++;
        src+=mirror;
    }
}
/**fun: pfb画垂直线*/
void SC_pfb_draw_line_y(SC_tile *dest,int xs,int ys,uint16_t *src,int len,int mirror)
{
    if(xs<dest->xs||xs> dest->xs+dest->w-1) return;     //x边界限定
    int st=  ys<dest->ys ? dest->ys  :ys;                              //ys边界限定
    int end= (ys+len)> (dest->ys+dest->h)? (dest->ys+dest->h):(ys+len);//ye边界限定
    uint16_t *pixel=((uint16_t*)dest->buf);
    int ofsx = xs-dest->xs;
    int ofsy = st-dest->ys;
    src+= mirror>=0? st-ys:len-1- (st-ys);            //src 偏移
    pixel+= ofsy*dest->w + ofsx;      //dest 偏移
    for(ys=st; ys<end; ys++)
    {
        *pixel= alphaBlend(gui->fc,gui->bkc,*src);
        pixel+= dest->w;
        src+=mirror;
    }
}



/**fun: 反锯齿画线函数
 *输入:
 *返回:
*/
void SC_DrawLine_AA(int x1, int y1, int x2, int y2,uint16_t colour)
{
    int dx = x2 - x1;
    int dy = y2 - y1;
    int sgndx = (dx < 0) ? -1 : 1;  // 确保x方向正确
    int sgndy = (dy < 0) ? -1 : 1;  // 确保y方向正确
    int dxabs = sc_abs(dx);  // 宽度
    int dyabs = sc_abs(dy);  // 高度
    uint8_t isHorizontal = dxabs > dyabs;  // 判断主方向
    int majorStep = isHorizontal ? dxabs : dyabs;  // 主方向步长
    int minorStep = isHorizontal ? dyabs : dxabs;  // 次方向步长
    int error = majorStep / 2;  // 初始化误差项
    uint16_t abuf[3];

    for (int i = 0; i <= majorStep; i++)
    {
        int a = (error << 8) / majorStep;  // 插值计算
        abuf[0]=alphaBlend(colour,0, 255-a);
        abuf[1]=colour;
        abuf[2]=alphaBlend(colour,0, a);
        if (isHorizontal)                   // 横向线条的插值
        {
            if(sgndy<0)
            {
                uint16_t temp= abuf[2];
                abuf[2]= abuf[0];
                abuf[0]=temp;
            }
            gui->Refresh(x1,y1-1,1,3,abuf);
            x1 += sgndx;                    // 更新x坐标
        }
        else                                // 纵向线条的插值
        {
            if(sgndx<0)
            {
                uint16_t temp= abuf[2];
                abuf[2]= abuf[0];
                abuf[0]=temp;
            }
            gui->Refresh(x1-1,y1,3,1,abuf);
            y1 += sgndy;                    // 更新y坐标
        }
        // 更新误差值
        error += minorStep;
        if (error >= majorStep)
        {
            error -= majorStep;
            // 更新次方向坐标
            if (isHorizontal)
            {
                y1 += sgndy;  // 横向时调整y
            }
            else
            {
                x1 += sgndx;  // 纵向时调整x
            }
        }
    }
}

#if DRAW_LINE_SDF_EN

//抗锯齿画直线，SDF算法圆头线
float PixelAlphaGain   = 255.0;
float LoAlphaTheshold  = 1.0/32.0;
float HiAlphaTheshold  = 1.0 - 1.0/32.0;
float capsuleSDF(float px, float py, float ax, float ay, float bx, float by, float r)
{
    float pax = px - ax, pay = py - ay, bax = bx - ax, bay = by - ay;
    float h = fmaxf(fminf((pax * bax + pay * bay) / (bax * bax + bay * bay), 1.0f), 0.0f);
    float dx = pax - bax * h, dy = pay - bay * h;
    return sqrtf(dx * dx + dy * dy) - r;
}
void SC_DrawLine_SDF(float ax, float ay, float bx, float by, float r,u16 fc)
{
    if(ax<0||bx>LCD_SCREEN_WIDTH) return;
    if(ay<0||by>LCD_SCREEN_HEIGHT) return;
    if (r < 0.0) return;
    int x0 = (int)floorf(fminf(ax, bx) - r);
    int x1 = (int) ceilf(fmaxf(ax, bx) + r);
    int y0 = (int)floorf(fminf(ay, by) - r);
    int y1 = (int) ceilf(fmaxf(ay, by) + r);
    float alpha=1.0;
    bool endX;
    int cnt=0;
    for (int y = y0; y <= y1; y++)
    {
        endX = 0;                                       // Flag to skip pixels
        for (int x = x0; x <= x1; x++)
        {
            alpha=0.5f - capsuleSDF(x, y, ax, ay, bx, by, r);
            if (!endX)
            {
                if(y0>ay)
                {
                    endX = true;
                    x0 = x;
                }
                if (alpha <= LoAlphaTheshold )continue;
            }
            else
            {
                if (alpha <= LoAlphaTheshold) break;   // Skip right side
            }
            alpha=(alpha * PixelAlphaGain);
            uint16_t pcol =alphaBlend( fc, 0,alpha>255?255:alpha);
            gui->bsp_pset(x, y, pcol);					//画点函数
        }
    }
}
#endif


